Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add raw_socket_proxy to directly proxy websockets to TCP/unix sockets #447

Merged
merged 7 commits into from
Jun 27, 2024

Conversation

dylex
Copy link
Contributor

@dylex dylex commented Feb 18, 2024

This incorporates the functionality of websockify by allowing websockets to be proxied to simple TCP or unix socket streams.

While I understand that websockify already provides a working solution to this problem (see discussion on #75), this seems to have a number of benefits, mainly that it eliminates the extra proxy layer and process that websockify imposes, improving latency and performance overall. It does so with a fairly small amount of code, much smaller and ismpler than websockify itself.

To simplify the instantiation of proxy handlers, this also generalizes and combines the _make_namedproxy_handler and _make_supervisedproxy_handler functions into a single _make_proxy_handler that dynamically determines the class to use, improving flexibility and reducing code duplication, at the cost of a little complexity.

Copy link

welcome bot commented Feb 18, 2024

Thanks for submitting your first pull request! You are awesome! 🤗

If you haven't done so already, check out Jupyter's Code of Conduct.
welcome
You can meet the other Jovyans by joining our Discourse forum. There is also a intro thread there where you can stop by and say Hi! 👋

Welcome to the Jupyter community! 🎉

dylex added a commit to flatironinstitute/jupyter-remote-desktop-proxy that referenced this pull request Feb 18, 2024
@manics
Copy link
Member

manics commented Apr 30, 2024

Thanks for the PR, and sorry for the delay in reviewing. I like the idea of having a self contained pure Python implementation! I can't guarantee we'll merge it since websockify works, seems reliable, and it's always nice if someone else maintains things, but #75 is several years old and I think we should consider this.

To help with review would you mind rebasing this onto main? We recently fixed a security vulnerability, so it would be helpful when testing locally if we could just checkout an up-to-date branch with your changes. Thanks!

This incorporates the functionality of websockify.  In doing so, it
eliminates an extra proxy layer and process, improving performance with
a fairly small amount of code.  Fixes jupyterhub#75.

This also generalizes and combines the _make_namedproxy_handler and
_make_supervisedproxy_handler functions into a single case to allow more
flexibility.
@dylex
Copy link
Contributor Author

dylex commented Apr 30, 2024

Thanks, yeah I understand the concerns, but having worked on the websockify code as well, I can at least say that this PR is overall much simpler and easier to maintain, and I'm happy to help do so.

I've rebased, and tested again on the updated version (with jupyter-remote-desktop-proxy) and things look okay. Probably there should be some automated tests for this, too -- I can look into that if there's interest in moving forward.

@manics
Copy link
Member

manics commented Apr 30, 2024

Thanks! Tests would be great if we go ahead but hold off for now. I'll try out your changes locally first, feel free to bump this issue if I forget!

@yuvipanda
Copy link
Contributor

Bump reminder, @manics :)

Copy link
Member

@manics manics left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in reviewing this. The code looks OK to me, but I don't have much experience with websockets so ideally it could do with a second review.

I've tested this with jupyter-remote-desktop-proxy and it works well!

diff --git a/jupyter_remote_desktop_proxy/setup_websockify.py b/jupyter_remote_desktop_proxy/setup_websockify.py
index c6b1f4b..f25801e 100644
--- a/jupyter_remote_desktop_proxy/setup_websockify.py
+++ b/jupyter_remote_desktop_proxy/setup_websockify.py
@@ -12,8 +12,6 @@ def setup_websockify():
         raise RuntimeError(
             "vncserver executable not found, please install a VNC server"
         )
-    if not which('websockify'):
-        raise RuntimeError("websockify executable not found, please install websockify")
 
     # TurboVNC and TigerVNC share the same origin and both use a Perl script
     # as the executable vncserver. We can determine if vncserver is TigerVNC
@@ -30,16 +28,10 @@ def setup_websockify():
         is_tigervnc = "tigervnc" in vncserver_file.read().casefold()
 
     if is_tigervnc:
-        # Make a secure temporary directory for sockets that is only readable,
-        # writeable, and searchable by our uid - TigerVNC can listen to a Unix
-        # socket!
-        sockets_dir = tempfile.mkdtemp()
-        sockets_path = os.path.join(sockets_dir, 'vnc-socket')
-
-        websockify_args = ['--unix-target', sockets_path]
-        vnc_args = [vncserver, '-rfbunixpath', sockets_path]
+        unix_socket = True
+        vnc_args = [vncserver, '-rfbunixpath', "{unix_socket}"]
     else:
-        websockify_args = []
+        unix_socket = False
         vnc_args = [vncserver, '-localhost', '-rfbport', '{port}']
 
     if not os.path.exists(os.path.expanduser('~/.vnc/xstartup')):
@@ -58,18 +50,13 @@ def setup_websockify():
     )
 
     return {
-        'command': [
-            'websockify',
-            '--verbose',
-            '--heartbeat=30',
-            '{port}',
-        ]
-        + websockify_args
-        + ['--', '/bin/sh', '-c', f'cd {os.getcwd()} && {vnc_command}'],
+        'command': ['/bin/sh', '-c', f'cd {os.getcwd()} && {vnc_command}'],
         'timeout': 30,
         'new_browser_window': True,
         # We want the launcher entry to point to /desktop/, not to /desktop-websockify/
         # /desktop/ is the user facing URL, while /desktop-websockify/ now *only* serves
         # websockets.
         "launcher_entry": {"title": "Desktop", "path_info": "desktop"},
+        "unix_socket": unix_socket,
+        "websockify": True,
     }

@yuvipanda what do you think?

image

@yuvipanda
Copy link
Contributor

I very much like the idea of not depending on websockify for TCP streams. This would allow us to package remote-desktop-proxy in conda-forge more easily too. I've re-opened #75 and will look through the code now.

Copy link
Contributor

@yuvipanda yuvipanda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do another round next week, but left two comments here!

I also think we need to add a test - perhaps similar to the existing websocket test, but with a simple TCP server?

Am really excited to get this in, as it would allow us to do a bunch of other cool stuff elsewhere :) For example, it would enable proper ssh forwarding in https://github.com/yuvipanda/jupyterhub-ssh...

Thank you for working on this, and your patience as we review this, @dylex!

jupyter_server_proxy/websockify.py Outdated Show resolved Hide resolved
docs/source/server-process.md Outdated Show resolved Hide resolved
Copy link
Contributor

@yuvipanda yuvipanda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent a little more time looking at this, and the overall structure seems fine to me. It does need a test (perhaps modeled after what our current websocket tests do, but with raw TCP). After that + the naming stuff, happy to merge.

Thank you for working through this, @dylex!

jupyter_server_proxy/websockify.py Outdated Show resolved Hide resolved
jupyter_server_proxy/websockify.py Outdated Show resolved Hide resolved
@dylex
Copy link
Contributor Author

dylex commented Jun 27, 2024

Okay, I think everything has been addressed now. Option is now named raw_socket_proxy, and there are a couple tests with a simple tcp and unix socket server backend. Let me know if there's anything I should cleanup/rebase.

@yuvipanda yuvipanda changed the title Allow proxying websockets over simple stream connections Add raw_socket_proxy to directly proxy websockets to TCP/unix sockets Jun 27, 2024
@yuvipanda
Copy link
Contributor

Alright, thank you very much for your work, @dylex! And sorry about the slow review. I hope to see more contributions from you in the future too <3

@yuvipanda yuvipanda merged commit 52b0dec into jupyterhub:main Jun 27, 2024
15 checks passed
@manics manics mentioned this pull request Jul 1, 2024
manics added a commit to manics/jupyter-remote-desktop-proxy that referenced this pull request Jul 4, 2024
jupyter-server-proxy 4.3.0 includes jupyterhub/jupyter-server-proxy#447
manics added a commit to manics/jupyter-remote-desktop-proxy that referenced this pull request Jul 5, 2024
jupyter-server-proxy 4.3.0 includes jupyterhub/jupyter-server-proxy#447
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants